﻿using System;
using System.Threading;

namespace NanoMvvm.Threading
{
    internal static class Locking
    {
        public static void GetReadLock(ReaderWriterLockSlim slimLock)
        {
            bool lockAcquired = false;
            while (!lockAcquired)
                lockAcquired = slimLock.TryEnterUpgradeableReadLock(1);
        }

        public static void GetReadOnlyLock(ReaderWriterLockSlim slimLock)
        {
            bool lockAcquired = false;
            while (!lockAcquired)
                lockAcquired = slimLock.TryEnterReadLock(1);
        }

        public static void GetWriteLock(ReaderWriterLockSlim slimLock)
        {
            bool lockAcquired = false;
            while (!lockAcquired)
                lockAcquired = slimLock.TryEnterWriteLock(1);
        }

        public static void ReleaseReadOnlyLock(ReaderWriterLockSlim slimLock)
        {
            if (slimLock.IsReadLockHeld)
                slimLock.ExitReadLock();
        }

        public static void ReleaseReadLock(ReaderWriterLockSlim slimLock)
        {
            if (slimLock.IsUpgradeableReadLockHeld)
                slimLock.ExitUpgradeableReadLock();
        }

        public static void ReleaseWriteLock(ReaderWriterLockSlim slimLock)
        {
            if (slimLock.IsWriteLockHeld)
                slimLock.ExitWriteLock();
        }

        public static void ReleaseLock(ReaderWriterLockSlim slimLock)
        {
            ReleaseWriteLock(slimLock);
            ReleaseReadLock(slimLock);
            ReleaseReadOnlyLock(slimLock);
        }

        public static ReaderWriterLockSlim GetLockInstance()
        {
            return GetLockInstance(LockRecursionPolicy.SupportsRecursion);
        }

        public static ReaderWriterLockSlim GetLockInstance(LockRecursionPolicy recursionPolicy)
        {
            return new ReaderWriterLockSlim(recursionPolicy);
        }
    }

    public struct ReadLock : IDisposable
    {
        private readonly ReaderWriterLockSlim slimLock;

        public ReadLock(ReaderWriterLockSlim @lock)
        {
            slimLock = @lock;
            Locking.GetReadLock(slimLock);
        }

        public void Dispose()
        {
            Locking.ReleaseReadLock(slimLock);
        }
    }

    public struct ReadOnlyLock : IDisposable
    {
        private readonly ReaderWriterLockSlim slimLock;

        public ReadOnlyLock(ReaderWriterLockSlim @lock)
        {
            slimLock = @lock;
            Locking.GetReadOnlyLock(slimLock);
        }

        public void Dispose()
        {
            Locking.ReleaseReadOnlyLock(slimLock);
        }
    }

    public struct WriteLock : IDisposable
    {
        private readonly ReaderWriterLockSlim slimLock;

        public WriteLock(ReaderWriterLockSlim @lock)
        {
            slimLock = @lock;
            Locking.GetWriteLock(slimLock);
        }

        public void Dispose()
        {
            Locking.ReleaseWriteLock(slimLock);
        }
    }
}